<?php

declare(strict_types=1);

namespace App\CustHub\Service;

use App\Chat\Constant\TalkModeConstant;
use App\Common\Robot\Robot;
use App\CustHub\Constants\MsgTypeConstants;
use App\CustHub\Constants\OpSchemeTypeConstants;
use App\CustHub\Helpers\TagReplacer;
use App\CustHub\Mapper\ChContactMapper;
use App\CustHub\Mapper\ChContactWhiteMapper;
use App\CustHub\Mapper\ChOpSchemeMapper;
use App\CustHub\Mapper\ChOpSchemeRobotMapper;
use App\CustHub\Mapper\ChRobotMapper;
use App\CustHub\Mapper\ChRoomInviteActiveRoomMapper;
use App\CustHub\Mapper\ChRoomMemberMapper;
use App\CustHub\Mapper\ChRoomMemberToKickMapper;
use App\CustHub\Model\ChChatRecord;
use App\CustHub\Model\ChContact;
use App\CustHub\Model\ChRobot;
use App\CustHub\Model\ChRoomMember;
use App\CustHub\Model\ChRoomMemberToKick;
use App\CustHub\Model\ChTalkRecord;
use App\CustHub\Traits\OpSchemeTrait;
use App\Robot\Dto\CommonJobDto;
use App\Robot\Job\CommonJob;
use App\Robot\Service\RobotApiService;
use App\Robot\Service\SendRobotMessageService;
use Carbon\Carbon;
use Hyperf\Di\Annotation\Inject;
use Mine\Abstracts\AbstractService;
use function Hyperf\Collection\data_get;
use function Hyperf\Support\call;
use function Hyperf\Support\now;

class ChRoomKickUserService extends AbstractService
{
    use OpSchemeTrait;

    public $mapper;

    private int $type = OpSchemeTypeConstants::ROOM_KICK_USER;

    #[Inject]
    private ChOpSchemeRobotMapper $schemeRobotMapper;

    #[Inject]
    private ChContactMapper $contactMapper;

    #[Inject]
    private ChRobotMapper $robotMapper;

    #[Inject]
    private ChContactWhiteMapper $whiteMapper;

    #[Inject]
    private ChConfigService $configService;

    #[Inject]
    private SendRobotMessageService $sendRobotMessageService;

    #[Inject]
    private ChRoomMemberToKickMapper $memberToKickMapper;

    #[Inject]
    private ChRoomMemberMapper $roomMemberMapper;

    #[Inject]
    private ChRoomMemberWarningService $kickUserWarningService;

    public function __construct(ChOpSchemeMapper $mapper)
    {
        $this->mapper = $mapper;
    }

    public function handleSaveData(array $data)
    {
        $count = $this->mapper->model::query()
            ->where('uid', $data['uid'])
            ->where('type', $this->type)
            ->count();
        if ($count == 0) {
            $data['name'] = "默认方案";
        } else {
            $data['name'] = "方案" . $count;
        }

        $cfg = data_get($data, 'cfg');
        if (!empty($cfg)) {
            $data['cfg'] = json_decode($data['cfg'], true);
        }
        return $data;
    }

    /**
     * 处理自动踢人
     * @param ChRobot $robot
     * @param array $data
     * @return void
     */
    public function handlePendingKick(ChRobot $robot, array $data): void
    {
        $roomWixd = $data['from_room_id'];
        $userId = $data['sender'];

        $info = $this->memberToKickMapper->getPendingKickInfo($roomWixd,$robot->id,$userId);

        if(!empty($info)){
            //撤回消息免踢
            $info->status = 2;
            $info->save();
        }
    }


    public function triggerHandler(ChRobot $robot, array $data)
    {
        $roomWixd = $data['from_room_id'];
        $userId = $data['sender'];
        //检查用户是否在白名单中
        if ($this->whiteMapper->checkContactExists($userId, $robot->main_uid)) {
            return;
        }

        $roomMemberInfo = ChRoomMember::query()
            ->select(['id','nickname','corp_id'])
            ->with(['corp'=>function ($query) {
                $query->select(['id','corp_name','corp_full_name']);
            }])
            ->where('room_wxid',$roomWixd)
            ->where('user_id',$userId)
            ->first();
        if (empty($roomMemberInfo)) {
            return;
        }

        $corpName = $roomMemberInfo->corp_name ?? '';
        $nickname = $roomMemberInfo->nickname;

        $config = $this->configService->getConfigByGroupKey($robot->uid, $robot->main_uid, 'room_auto_kick_user');

        $schemeList = $this->schemeRobotMapper->findRobotScheme($robot->id, $this->type);
        $now = time();
        foreach ($schemeList as $robotScheme) {
            $scheme = $robotScheme->scheme;
            if(empty($scheme)){
                continue;
            }
            $cfg = $scheme->cfg;
            $start_time = Carbon::parse(Carbon::now()->toDateString().' '.$cfg['start_time'])->timestamp;
            $end_time = Carbon::parse(Carbon::now()->toDateString().' '.$cfg['end_time'])->timestamp;
            if($now < $start_time || $now > $end_time){
                continue;
            }
            //直接踢人
            $kickUserConfig = $cfg['kick_user'];
            if ($kickUserConfig['switch'] == 1) {
                if (!$this->checkInWhiteList($kickUserConfig, $robot, $corpName, $config)) {
                    continue;
                }
                $rst = $this->check($kickUserConfig,$config, $robot, $roomWixd, $userId, $data, $nickname);
                if ($rst['flag']) {
                    //判断是否开启撤回免踢
                    $rev_msg_no_kick = data_get($kickUserConfig, 'rev_msg_no_kick', 0);
                    $exempt_time = data_get($kickUserConfig, 'exempt_time', 0);
                    if ($rev_msg_no_kick == 1) {
                        $this->revMsgNoKick($robot,$kickUserConfig,$nickname,$rst,$userId,$roomWixd,$exempt_time);
                    }else{
                        //直接从群聊中移除
                        $this->removeRoomMember($roomWixd,$robot->id,$userId);
                    }
                    //插入需要踢除的信息
                    continue;
                }
            }

            //踢人加警告
            $kickWarnUserConfig = data_get($cfg,'kick_warn_user');
            if ($kickWarnUserConfig['switch'] == 1) {
                if (!$this->checkInWhiteList($kickWarnUserConfig, $robot, $corpName, $config)) {
                    continue;
                }
                $rst = $this->check($kickWarnUserConfig,$config, $robot, $roomWixd, $userId, $data, $nickname);
                if ($rst['flag']) {
                    //判断是否开启撤回免踢
                    $rev_msg_no_kick = data_get($kickWarnUserConfig, 'rev_msg_no_kick', 0);
                    $exempt_time = data_get($kickWarnUserConfig, 'exempt_time', 0);
                    if ($rev_msg_no_kick == 1) {
                        $this->revMsgNoKick($robot,$kickUserConfig,$nickname,$rst,$userId,$roomWixd,$exempt_time);
                    }else{
                        $kickMsg = [];
                        if(!empty($kickWarnUserConfig['kick_msg'])){
                            $kickMsg = $this->changeSendMsg($kickWarnUserConfig['kick_msg'],$nickname,$rst['msg'],$userId);
                        }
                        //直接从群聊中移除
                        $this->removeRoomMember($roomWixd,$robot->id,$userId,$kickMsg);
                    }
                    //插入需要踢除的信息
                    continue;
                }
            }

            //警告后踢人
            $kickAfterWarnConfig = data_get($cfg,'kick_after_warn');
            if($kickAfterWarnConfig['switch'] == 1){
                if (!$this->checkInWhiteList($kickAfterWarnConfig, $robot, $corpName, $config)) {
                    continue;
                }
                $rst = $this->check($kickAfterWarnConfig,$config, $robot, $roomWixd, $userId, $data, $nickname);
                if ($rst['flag']) {
                    $cnt = data_get($kickAfterWarnConfig,'warning_cnt.cnt',1);
                    $unit = data_get($kickAfterWarnConfig,'warning_cnt.unit',1);
                    $value = data_get($kickAfterWarnConfig,'warning_cnt.value',1);
                    $shouldKickUser = $this->kickUserWarningService->handleViolation($robot->uid,$robot->main_uid,$roomWixd,$userId,$robot->id,$unit,$value,$cnt);
                    if($shouldKickUser){
                        $kickMsg = [];
                        if(!empty($kickWarnUserConfig['kick_msg'])){
                            $kickMsg = $this->changeSendMsg($kickWarnUserConfig['kick_msg'],$nickname,$rst['msg'],$userId);
                        }
                        //直接从群聊中移除
                        $this->removeRoomMember($roomWixd,$robot->id,$userId,$kickMsg);
                    }else{
                        //发送警告话术
                        if(!empty($kickWarnUserConfig['warning_msg'])){
                            $warningMsg = $this->changeSendMsg($kickWarnUserConfig['warning_msg'],$nickname,$rst['msg'],$userId);
                            //发送违规消息
                            foreach ($warningMsg as $item){
                                $this->sendRobotMessageService->handle([
                                    'robot_id' => $robot->id,
                                    'msg_type' => $item['msg_type'],
                                    'content' => $item['content'],
                                    'to_id' => $roomWixd
                                ]);
                            }
                        }
                    }
                    //插入需要踢除的信息
                    continue;
                }
            }
            //只警告
            $warnOnlyConfig = data_get($cfg,'warning_only');
            if($warnOnlyConfig['switch'] == 1){
                if (!$this->checkInWhiteList($warnOnlyConfig, $robot, $corpName, $config)) {
                    continue;
                }
                $rst = $this->check($warnOnlyConfig,$config, $robot, $roomWixd, $userId, $data, $nickname);
                if ($rst['flag']) {
                    //发送警告话术
                    if(!empty($warnOnlyConfig['warning_msg'])){
                        $warningMsg = $this->changeSendMsg($warnOnlyConfig['warning_msg'],$nickname,$rst['msg'],$userId);
                        //发送违规消息
                        foreach ($warningMsg as $item){
                            $this->sendRobotMessageService->handle([
                                'robot_id' => $robot->id,
                                'msg_type' => $item['msg_type'],
                                'content' => $item['content'],
                                'to_id' => $roomWixd
                            ]);
                        }
                    }
                }
            }
        }
    }

    /**
     * 撤回消息后免踢
     * @param ChRobot $robot
     * @param array $config
     * @param string $nickname
     * @param array $rst
     * @param string $userId
     * @param string $roomWixd
     * @param int $exempt_time
     * @return void
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    private function revMsgNoKick(ChRobot $robot,array $config,string $nickname,array $rst,string $userId,string $roomWixd,int $exempt_time): void
    {
        $reminder_msg = data_get($config, 'reminder_msg', '');
        if(!empty($reminder_msg)){
            $reminder_msg = $this->changeSendMsg($reminder_msg,$nickname,$rst['msg'],$userId);

            //发送违规消息
            foreach ($reminder_msg as $item){
                $this->sendRobotMessageService->handle([
                    'robot_id' => $robot->id,
                    'msg_type' => $item['msg_type'],
                    'content' => $item['content'],
                    'to_id' => $roomWixd
                ]);
            }

            //存储待踢人信息
            $kickData = [
                'uid' => $robot->uid,
                'main_uid' => $robot->main_uid,
                'from_room_id' => $roomWixd,
                'robot_id' => $robot->id,
                'user_id' => $userId,
                'end_time' => Carbon::now()->addSeconds($exempt_time)->timestamp
            ];
            $id = $this->memberToKickMapper->save($kickData);

            //延迟队列处理
            container()->get(LowQueueService::class)->push(new CommonJob(new CommonJobDto(
                classname: ChRoomKickUserService::class,
                method: 'kickHandler',
                data:[
                    'id' => $id
                ]
            )),$exempt_time);
        }
    }

    /**
     * 替换消息
     * @param string $content
     * @param string $nickname
     * @param string $errMsg
     * @param string $userId
     * @return array
     */
    private function changeSendMsg(string $content,string $nickname,string $errMsg,string $userId): array
    {
        $reminder_msg = [
            [
                'msg_type' => 'text',
                'content' => $content
            ]
        ];
        $reminder_msg = TagReplacer::changeData($reminder_msg, [
            'nickname' => $nickname,
            'violation_reason' => $errMsg,
            'random_emoji' => '',
            'receiver' => $userId
        ]);
        return $reminder_msg;
    }

    /**
     * 踢人处理
     * @param array $data
     * @return void
     */
    public function kickHandler(array $data): void
    {
        $info = $this->memberToKickMapper->findById($data['id']);
        if(!empty($info) && $info->status == 0){
            //将用户从群聊中移除
            $this->removeRoomMember($info->from_room_id,$info->robot_id,$info->user_id);
            $info->status = 1;
            $info->save();
        }
    }

    /**
     * 移除群聊成员
     * @param string $fromRoomId
     * @param int $robotId
     * @param string $userId
     * @return void
     */
    private function removeRoomMember(string $fromRoomId,int $robotId,string $userId,array $kickMsg = []): void
    {
        $robot = $this->robotMapper->findById($robotId);
        if(!empty($robot) && $robot['login_status'] == 1){
            $robotApiService = make(Robot::class, [$robot->server_id, $robot->uuid])->gateway();
            if(!empty($kickMsg)){
                //发送违规消息
                foreach ($kickMsg as $item){
                    $this->sendRobotMessageService->handle([
                        'robot_id' => $robot->id,
                        'msg_type' => $item['msg_type'],
                        'content' => $item['content'],
                        'to_id' => $fromRoomId
                    ]);
                }
            }
            $rst = $robotApiService->removeRoomMember($fromRoomId,[$userId]);
            if($rst !== false){
                //变更用户在群状态
                $this->roomMemberMapper->outRoom($fromRoomId,[$userId]);
            }
        }
    }

    /**
     * 检查是否在白名单中
     * @param array $ruleConfig
     * @param ChRobot $robot
     * @param string $corpName
     * @param array $config
     * @return bool
     */
    private function checkInWhiteList(array $ruleConfig, ChRobot $robot, string $corpName, array $config): bool
    {
        //ID下登录的企业自动默认为白名单企业
        if ($corpName == $robot->corp_name) {
            return false;
        }

        //踢非白名单企业的企业微信
        $wechat_type = data_get($ruleConfig, 'wechat_type.switch', 0);
        if ($wechat_type == 1 && !empty($config['corp_white_list']['value'])) {
            $corp_white_list = explode("\n", $config['corp_white_list']['value']);
            if (in_array($corpName, $corp_white_list)) {
                return false;
            }
        }
        return true;
    }

    private function check(array $ruleConfig,array $whiteListConfig, ChRobot $robot, string $roomWixd, string $userId, array $data, string $nickname): array
    {

        $content = data_get($data, 'content.content');
        $msg_type = data_get($data, 'content.msg_type');

        $keyword_trigger = data_get($ruleConfig, 'keyword_trigger', []);
        $msg_keyword_switch = data_get($keyword_trigger, 'msg_keyword.switch', 0);

        if ($msg_keyword_switch == 1 && $msg_type == MsgTypeConstants::TEXT) {
            //模糊关键词
            $fuzzy_keywords = data_get($keyword_trigger, 'msg_keyword.fuzzy_keywords', []);
            if (!empty($fuzzy_keywords)) {
                $fuzzy_keywords_result = checkKeywords($fuzzy_keywords, $content);
                if ($fuzzy_keywords_result) {
                    return [
                        'flag' => true,
                        'msg' => '发送内容违规'
                    ];
                }
            }
            //精确关键词
            $exact_keywords = data_get($keyword_trigger, 'msg_keyword.exact_keywords', []);
            if (!empty($exact_keywords)) {
                $exact_keywords_result = checkKeywords($exact_keywords, $content, 1);
                if ($exact_keywords_result) {
                    return [
                        'flag' => true,
                        'msg' => '发送内容违规'
                    ];
                }
            }
        }

        $nickname_keyword_switch = data_get($keyword_trigger, 'nickname_keyword.switch', 0);
        if ($nickname_keyword_switch == 1) {
            //昵称关键词
            $nickname_keywords = data_get($keyword_trigger, 'nickname_keyword.nickname_keyword', []);
            if (!empty($nickname_keywords)) {
                $nickname_keywords_result = checkKeywords($nickname_keywords, $nickname);
                if ($nickname_keywords_result) {
                    return [
                        'flag' => true,
                        'msg' => '昵称违规'
                    ];
                }
            }
        }
        //发广告
        $send_adv = data_get($ruleConfig, 'send_adv', []);
        if (!empty($send_adv)) {
            $advType = $this->changeMsgType($data['msgtype']);
            if(in_array($advType, $send_adv)){
                $wechat_type = data_get($ruleConfig, 'wechat_type.switch', 0);
                $advFlag = true;
                //白名单
                if($wechat_type == 1){
                    //网址白名单
                    $url_white_list = explode("\n", $whiteListConfig['url_white_list']['value']);
                    if(!empty($url_white_list) && $advType == 4){
                        $sendUrl = data_get($data,'msg_data.link_url','');
                        foreach ($url_white_list as $item){
                            if(str_contains($sendUrl, $item)){
                                $advFlag = false;
                                break;
                            }
                        }
                    }

                    if($advFlag){
                        $xcx_white_list = explode("\n", $whiteListConfig['xcx_white_list']['value']);
                        if(!empty($xcx_white_list) && $advType == 5){
                            $appid = data_get($data,'msg_data.appid','');
                            $title =  data_get($data,'msg_data.title','');
                            foreach ($xcx_white_list as $item){
                                if($title == $item || $appid == $item){
                                    $advFlag = false;
                                    break;
                                }
                            }
                        }
                    }

                    if($advFlag){
                        $sph_white_list = explode("\n", $whiteListConfig['sph_white_list']['value']);
                        if(!empty($sph_white_list) && $advType == 6){
                            $title =  data_get($data,'msg_data.channel_name','');
                            foreach ($sph_white_list as $item){
                                if($title == $item){
                                    $advFlag = false;
                                    break;
                                }
                            }
                        }
                    }
                }

                if($advFlag){
                    return [
                        'flag' => true,
                        'msg' => $this->adv($data['msgtype'])
                    ];
                }
            }
        }

        //发骚扰消息
        $send_spam_msg = data_get($ruleConfig, 'send_spam_msg', []);
        $flood_chat = data_get($send_spam_msg, 'flood_chat', []);

        $flood_chat_one_switch = data_get($flood_chat, 'one.switch', 0);
        stdout_log()->info('炸群调试1');
        if ($flood_chat_one_switch == 1) {
            //发送消息条数
            $checkMsg = $this->checkSendMsgNum($flood_chat, 'one', $robot->id, $roomWixd, $userId, MsgTypeConstants::TEXT);
            if ($checkMsg['flag']) {
                //满足条件
                return [
                    'flag' => true,
                    'msg' => $checkMsg['msg']
                ];
            }
        }

        $flood_chat_two_switch = data_get($flood_chat, 'two.switch', 0);
        if ($flood_chat_two_switch == 1) {
            //发送表情条数
            $checkMsg = $this->checkSendMsgNum($flood_chat, 'one', $robot->id, $roomWixd, $userId, MsgTypeConstants::EMOJI);
            if ($checkMsg['flag']) {
                //满足条件
                return [
                    'flag' => true,
                    'msg' => $checkMsg['msg']
                ];
            }
        }

        //炸群
        $disrupt_room = data_get($send_spam_msg, 'disrupt_room', []);
        $disrupt_room_msg_switch = data_get($disrupt_room, 'msg_switch', 0);
        if ($disrupt_room_msg_switch == 1) {
            //消息大于多少行
            $msg_num = data_get($disrupt_room, 'msg_num', 0);
            $contentRowNum = explode('\n', $content);
            if (count($contentRowNum) > $msg_num) {
                //满足条件
                return [
                    'flag' => true,
                    'msg' => "发送的内容大于{$msg_num}行"
                ];
            }
        }

        $disrupt_room_character_switch = data_get($disrupt_room, 'character_switch', 0);
        if ($disrupt_room_character_switch == 1) {
            //消息大于多少个字
            $character_num = data_get($disrupt_room, 'character_num', 0);
            if (strlen($content) > $character_num) {
                //满足条件
                return [
                    'flag' => true,
                    'msg' => "发送的内容大于{$character_num}个字符"
                ];
            }
        }

        return [
            'flag' => false,
            'msg' => ""
        ];
    }

    /**
     * @param array $flood_chat
     * @param string $keyPrefix
     * @param int $robotId
     * @param string $roomWixd
     * @param string $userId
     * @param string $msgType
     * @param int $checkType
     * @return array
     */
    private function checkSendMsgNum(array $flood_chat, string $keyPrefix, int $robotId, string $roomWixd, string $userId, string $msgType,int $checkType = 0): array
    {
        stdout_log()->info('炸群调试2');
        //发送消息条数
        $seconds = data_get($flood_chat, $keyPrefix . '.min', 0);
        $num = data_get($flood_chat, $keyPrefix . '.max', 0);
        $startTime = Carbon::now()->subSeconds($seconds)->toDateTimeString();
        $sendNum = ChTalkRecord::query()
            ->where('talk_type', TalkModeConstant::GROUP_CHAT)
            ->where('robot_id',$robotId)
            ->where('from_room_id',$roomWixd)
            ->where('msg_type',$msgType)
            ->where('sender',$userId)
            ->where('is_cs',0)
            ->where('created_at', '>=', $startTime)
            ->count();
        stdout_log()->info('炸群调试3'.'---'.$sendNum);
        if ($sendNum > $num) {
            //满足条件
            return [
                'flag' => true,
                'msg' => $checkType == 0 ? "{$seconds}秒内发送{$num}条信息" : "{$seconds}秒内发送{$num}条表情"
            ];
        }
        return [
            'flag' => false,
            'msg' => ''
        ];
    }


    private function changeMsgType(int $msgType)
    {
        match ($msgType) {
//            0,2 => $msgType = 1,//二维码
            41 => $msgType = 2,//名片
            23, 103 => $msgType = 3,//小视频
            13 => $msgType = 4,//网址分享
            78 => $msgType = 5,//小程序
            141 => $msgType = 6,//视频号
            4 => $msgType = 7,//聊天合集
            15, 102, 20 => $msgType = 9,//文件
            213 => 10,//接龙
            default => $msgType = 0,
        };
        return $msgType;
    }

    private function adv(int $msgType)
    {
        match ($msgType) {
            0,2 => $msgType = '发送二维码',//二维码
            41 => $msgType = '分享名片',//名片
            23, 103 => $msgType = '发送小视频',//小视频
            13 => $msgType = '分享网址',
            78 => $msgType = '发送小程序',//小程序
            141 => $msgType = '发送视频号',//视频号
            4 => $msgType = '发送聊天合集',//聊天合集
            15, 102, 20 => $msgType = '发送文件',//文件
            213 => '发送群接龙',//接龙
            default => $msgType = '',
        };
        return $msgType;
    }
}